////////////////////////////////////////////////////////////////////////////////

#include "client_gui.h"

////////////////////////////////////////////////////////////////////////////////

#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <boost/bind.hpp>
#include "client.h"
#include "network_packets.h"

////////////////////////////////////////////////////////////////////////////////


ClientGUI::ClientGUI( Client* _client, boost::asio::io_service& _io_service )
: m_Screen(NULL),
  m_Player(NULL),
  m_Bullet(NULL),
  m_io_service(_io_service),
  m_Timer(m_io_service),
  m_Client(_client)
{
    // Init SDL
    SDL_Init( SDL_INIT_EVERYTHING );

    const int width = 640;
    const int height = 480;
    const int bitsPerPixel = 32;

    //Set up the screen
    m_Screen = SDL_SetVideoMode( width, height, bitsPerPixel, SDL_HWSURFACE | SDL_DOUBLEBUF);

    //Set the window caption
    SDL_WM_SetCaption( "Shoot the music", NULL );

    // Load player image
    m_Player = LoadImage("player.png");

    // Load player image
    m_Bullet = LoadImage("bullet.png");

    // Start GUI
    Start();
}


////////////////////////////////////////////////////////////////////////////////


ClientGUI::~ClientGUI()
{
    SDL_Quit();
}


////////////////////////////////////////////////////////////////////////////////


void ClientGUI::Start()
{
    // SDL event
    SDL_Event event;

    // Process user input
    while( SDL_PollEvent(&event) )
    {
        if (event.type == SDL_QUIT)
            throw "quit";
        ProcessEvent(event);
    }

    // Send events to server
    m_Client->SendEvents();

    m_Timer.expires_from_now( boost::posix_time::milliseconds(1) );
    m_Timer.async_wait( boost::bind(&ClientGUI::Start, this) );
}


////////////////////////////////////////////////////////////////////////////////


void ClientGUI::ProcessEvent( const SDL_Event& _event )
{
    // ClientEngine event
    STM::Event engineEvent = STM::INVALID;

    // Convert from SDL to internal engine event
    switch (_event.type)
    {
    case SDL_KEYDOWN:
        {
            switch (_event.key.keysym.sym)
            {
            case SDLK_w:
                engineEvent = STM::W_DOWN;
                break;
            case SDLK_s:
                engineEvent = STM::S_DOWN;
                break;
            case SDLK_a:
                engineEvent = STM::A_DOWN;
                break;
            case SDLK_d:
                engineEvent = STM::D_DOWN;
                break;
            default:
                break;
            }

            break;
        }

    ////////////////////////////////////////

    case SDL_KEYUP:
        {
            switch (_event.key.keysym.sym)
            {
            case SDLK_w:
                engineEvent = STM::W_UP;
                break;
            case SDLK_s:
                engineEvent = STM::S_UP;
                break;
            case SDLK_a:
                engineEvent = STM::A_UP;
                break;
            case SDLK_d:
                engineEvent = STM::D_UP;
                break;
            default:
                break;
            }

            break;
        }

    ////////////////////////////////////////

    case SDL_MOUSEBUTTONDOWN:
        {
            switch (_event.button.button)
            {
            case SDL_BUTTON_LEFT:
                engineEvent = STM::MOUSE1_DOWN;
                break;
            default:
                break;
            }
            break;
        }

    ////////////////////////////////////////

    case SDL_MOUSEBUTTONUP:
        {
            switch (_event.button.button)
            {
            case SDL_BUTTON_LEFT:
                engineEvent = STM::MOUSE1_UP;
                break;
            default:
                break;
            }
            break;
        }

    ////////////////////////////////////////

    case SDL_MOUSEMOTION:
        m_Client->ProcessMouseMotion(_event.motion.x, _event.motion.y);
        break;

    default:
        return;
    }

    m_Client->ProcessEvent(engineEvent);
}


////////////////////////////////////////////////////////////////////////////////


void ClientGUI::Draw( const ServerPacket& _packet )
{
    // Ensure images are loaded
    if (m_Screen && m_Player && m_Bullet)
    {
        // Fill the screen white
        SDL_FillRect( m_Screen,
                      &m_Screen->clip_rect,
                      SDL_MapRGB( m_Screen->format, 0xFF, 0xFF, 0xFF ) );

        // Draw players
        for (size_t i = 0; i < _packet.m_Players.size(); ++i)
        {
            int x = _packet.m_Players[i].first - m_Player->w/2;
            int y = _packet.m_Players[i].second - m_Player->h/2;

            ApplySurface(x, y, m_Player, m_Screen);
        }

        // Draw bullets
        for (size_t i = 0; i < _packet.m_Bullets.size(); ++i)
        {
            int x = _packet.m_Bullets[i].first - m_Bullet->w/2;
            int y = _packet.m_Bullets[i].second - m_Bullet->h/2;

            ApplySurface(x, y, m_Bullet, m_Screen);
        }

        // Update the screen
        SDL_Flip( m_Screen );
    }
}


////////////////////////////////////////////////////////////////////////////////


SDL_Surface* ClientGUI::LoadImage( std::string _fileName )
{
    //The image that's loaded
    SDL_Surface* loadedImage = NULL;

    //The optimized surface that will be used
    SDL_Surface* optimizedImage = NULL;

    //Load the image
    loadedImage = IMG_Load( _fileName.c_str() );

    //If the image loaded
    if( loadedImage != NULL )
    {
        //Create an optimized surface
        optimizedImage = SDL_DisplayFormat( loadedImage );

        //Free the old surface
        SDL_FreeSurface( loadedImage );

        //If the surface was optimized
        if( optimizedImage != NULL )
        {
            //Color key surface
            SDL_SetColorKey( optimizedImage, SDL_SRCCOLORKEY, SDL_MapRGB( optimizedImage->format, 0xFF, 0xFF, 0xFF ) );
        }
    }

    //Return the optimized surface
    return optimizedImage;
}


////////////////////////////////////////////////////////////////////////////////


void ClientGUI::ApplySurface( int x,
                        int y,
                        SDL_Surface* source,
                        SDL_Surface* destination,
                        SDL_Rect* clip /* = NULL */ )
{
    //Holds offsets
    SDL_Rect offset;

    //Get offsets
    offset.x = x;
    offset.y = y;

    //Blit
    SDL_BlitSurface( source, clip, destination, &offset );
}


////////////////////////////////////////////////////////////////////////////////
